<template>
	<view class="ifun-tree-picker">
		<view class="picker-label" @click="handleShowPicker">
			<slot>
				<text v-if="selectText">{{selectText}}</text>
				<text v-else class="picker-label-placeholder">{{placeholder}}</text>
			</slot>
		</view>
		<view class="picker-popup" v-show="visible">
			<view class="picker-popup-mask" @click="handleCancel" :class="[beforeClose?'hidden':'']"></view>
			<view class="picker-popup-body" :class="[beforeClose?'hidden':'']">
				<view class="picker-header">
					<slot name="header">
						<button class="cancel" @click="handleCancel">取消</button>
						<view class="header-search" v-if="filterable">
							<input type="text" inputmode="search" confirm-type="search" auto-blur hold-keyboard
								placeholder="输入内容搜索" @confirm="handleSearch" />
						</view>
						<button class="submit" @click="handleConfirm">确定</button>
					</slot>
				</view>
				<picker-view :value="selectIndex" class="picker-content" @change="handleChangePicker">
					<picker-view-column v-for="(arr,index) in viewData" :key="index">
						<view class="picker-column-item" v-for="item in arr" :key="item[options.value]">
							<text>{{ item[options.label] }}</text>
						</view>
					</picker-view-column>
				</picker-view>
			</view>
		</view>
	</view>
</template>

<script>
	// 节点信息映射
	const DefaultOptions = {
		label: 'label',
		value: 'value',
		key: 'label',
		children: 'children'
	}
	export default {
		name: 'ifun-tree-picker',
		props: {
			disabled: Boolean,
			data: {
				type: Array,
				default: () => ([])
			},
			treeOptions: {
				type: Object,
				defautl: () => ({})
			},
			value: {
				type: [Object, Array],
				default: () => ({})
			},
			clearable: Boolean,
			checkStrictly: Boolean,
			placeholder: {
				type: String,
				default: '请选择'
			},
			valueFormat: Function,
			lazy: {
				type: Boolean,
				default: false
			},
			load: Function,
			// 搜索
			filterable: Boolean,
			filterMethod: Function
		},
		data() {
			return {
				selectValue: [],
				// picker 的自身属性
				selectIndex: [],
				// 当前的节点数据
				viewData: [],
				beforeClose: false,
				visible: false,
			};
		},
		watch: {
			data: {
				immediate: true,
				deep: true,
				handler(arr) {
					this.init(arr);
				}
			},
			value: {
				// immediate: true,
				deep: true,
				handler() {
					// 处理默认值
					this.handleFormatDefault(this.value);
					this.viewData.length = 1;
					// 初始化数据
					this.initViewData();
				}
			}
		},
		computed: {
			options() {
				return {
					...DefaultOptions,
					...this.treeOptions
				}
			},
			selectText() {

				let len = this.selectValue.length
				if (len < 1) {
					return ''
				}
				if (this.valueFormat && typeof this.valueFormat == 'function') {
					return this.valueFormat(this.selectValue)
				}
				return this.selectValue[len - 1][this.options.label]
			},
		},
		methods: {
			init(data) {

				// 默认第一条数据
				this.viewData = [JSON.parse(JSON.stringify(data))]
				// this.viewData = [arr];
				// 是否严格遵循父子节点不关联
				if (this.checkStrictly) {
					this.handleFormatData(this.viewData[0]);
				}
				if (this.clearable) {
					const {
						value,
						label
					} = this.options
					this.viewData[0].unshift({
						[value]: '',
						[label]: ''
					})
				}

				// 处理默认值
				this.handleFormatDefault(this.value);
				// 初始化后续的数据
				this.initViewData();
			},
			handleSearch({
				detail
			}) {

				const {
					value
				} = detail;
				if (this.filterMethod && typeof this.filterMethod === 'function') {
					// 自定义搜索
					this.filterMethod(value);
					return
				}
				if (value === '') {
					this.init(this.data);
					return
				}
				const {
					label
				} = this.options

				let data = this.data.filter(item => item[label].includes(value));

				this.init(data);
			},
			handleShowPicker() {
				if (this.disabled) {
					return
				}
				this.visible = true;

			},
			/**
			 * 使用picker-view实现的,计算变更的是哪列
			 */
			handleChangePicker({
				detail
			}) {

				const {
					value
				} = detail;
				let len = value.length;

				let column = 0;
				for (let i = 0; i < len; i++) {
					if (value[i] != this.selectIndex[i]) {
						column = i;
						break;
					}
				}

				this.handleChange({
					detail: {
						column,
						value: value[column]
					}
				});
			},
			handleCancel() {
				this.beforeClose = true;
				setTimeout(() => {
					this.visible = false;
					this.beforeClose = false;
				}, 400)
			},
			/**
			 * 
			 */
			handleFormatData(data) {

				let len = data.length;
				const {
					children,
					value,
					label
				} = this.options
				for (let i = 0; i < len; i++) {
					let item = data[i]
					if (item[children] && item[children].length > 0) {
						this.handleFormatData(item[children])
						item[children].unshift({
							[value]: '',
							[label]: ''
						});
					}
				}
			},
			/**
			 * 处理默认值是数组时的情况
			 * 解决不同父级，相同子级的数据展示问题
			 * @param {Array} arr
			 */
			handleFormatDefaultOfArray(arr) {
				let len = arr.length;
				if (len < 1) {
					return
				}
				const {
					value,
					children
				} = this.options
				// 
				let selectData = this.viewData[0];
				let selectIndexs = [];
				let selectValues = [];
				for (let i = 0; i < len; i++) {
					let info = arr[i];
					let index = selectData.findIndex(item => item[value] == info[value])

					if (index > -1) {
						selectIndexs.push(index);
						selectValues.push(selectData[index]);
						selectData = selectData[index][children] || [];
					}
				}
				if (selectIndexs.length < 1) {
					// 没有找到对应的数据，直接显示给定值
					this.selectValue = arr;
					return
				}
				this.selectIndex = [...selectIndexs];
				this.selectValue = [...selectValues];
			},
			/**
			 * 
			 * @param {Object} info
			 */
			handleFormatDefault(info) {
				this.selectValue = [];
				this.selectIndex = [];
				let $this = this;

				// 接受默认值参数为数组，填充默认值
				if (Array.isArray(info)) {
					this.handleFormatDefaultOfArray(info)
					return
				}
				const {
					value,
					children
				} = this.options;

				let selectedValue = info[value]
				if (selectedValue === undefined) {
					return;
				}
				/**
				 * 递归查找选中的数据
				 * @param {Object} p
				 * @param {Object} index
				 */
				function deepFind(p, index) {

					if (p[value] == selectedValue) {
						return [
							[p],
							[index]
						];
					}
					if (!p[children] || p[children].length < 1) {
						return [];
					}
					let len = p[children].length;
					let res = []

					for (let i = 0; i < len; i++) {
						const [values, indexs] = deepFind(p[children][i], i);
						if (values && values.length > 0) {
							res = [
								[p],
								[index]
							]
							res[0].push(...values);
							res[1].push(...indexs);
							break;
						}
					}

					return res;
				}
				let data = this.viewData[0];
				for (let i = 0, len = data.length; i < len; i++) {
					let item = data[i]

					let [values, indexs] = deepFind(item, i);
					if (values && values.length > 0) {
						// arr.push(info);
						this.selectValue = values
						this.selectIndex = indexs
						return
					}
				}
				// 没有找到对应的数据，直接显示给定值
				this.selectValue = [info]

			},
			initViewData() {
				const {
					children
				} = this.options

				if (this.selectIndex.length < 1) {
					this.selectIndex.push(0);
				}
				if (this.clearable && this.selectIndex[0] == 0) {
					// 可清除，默认选择的第一个
					return;
				}
				let data = this.viewData[0];
				let index = 0;
				let info = data[this.selectIndex[index]];
				// 默认值选中
				while (info && info[children] && info[children].length > 0) {
					let nextData = info[children];
					// 默认第一条数据
					this.viewData.push(nextData)
					// 处理默认值
					index++;
					if (this.selectIndex[index] !== undefined) {
						// 继续处理下一层级的选择数据
						info = nextData[this.selectIndex[index]]
					} else {
						info = nextData[0];
						this.selectIndex.push(0);
					}
				}

				// 数据初始化后，调用一次懒加载

				if (this.lazy) {
					let columnIndex = 0;
					if (this.selectIndex.length > 0) {
						columnIndex = this.selectIndex.length - 1;
					}
					this.handleLazyLoad({
						column: columnIndex,
						value: 0
					})
				}
			},
			/**
			 * 异步加载时，逻辑处理，数据由内部直接处理
			 * 
			 */
			handleLazyLoad({
				column
			}) {

				let $this = this;
				const {
					value,
					label
				} = this.options

				// 解决 不同父级、同一子级的问题，懒加载后默认选中该子级
				// this.selectIndex.length = column + 1;
				this.viewData.length = column + 1;

				// let selectValues = this.selectIndex.map((val, index) => this.viewData[index][val]);
				let selectValues = this.viewData.map((items, index) => items[this.selectIndex[index]]);

				if (!selectValues[column]) {
					return
				}
				this.load(selectValues[column], selectValues, (data) => {

					// 回调、处理异步加载数据
					if (Array.isArray(data) && data.length > 0) {

						// 是否严格遵循父子节点不关联
						if ($this.checkStrictly) {
							data.unshift({
								[value]: '',
								[label]: ''
							});
						}

						$this.viewData.push(data);

						// 处理默认值
						// 优先使用 this.selectIndex ；没有选中值，再用默认值value
						/**
						 * 2024-03-04 
						 * 修复：查找懒加载数据中是否存在之前选中的值，没有则默认选中 第一条数据 0 ；
						 * 
						 */

						let selectIndex = -1;
						let isInitSelect = false;
						if ($this.selectIndex[column + 1] !== undefined) {
							// 数据中是否存在之前选中数据
							selectIndex = data.findIndex(item => item[value] == $this.selectValue[column + 1][
								value
							])
							// selectIndex = $this.selectIndex[column+1];
						} else if ($this.value[column + 1] !== undefined) {
							// 处理默认选中
							selectIndex = data.findIndex(item => item[value] == $this.value[column + 1][value]);
							isInitSelect = true;
						}
						if (selectIndex < 0) {
							selectIndex = 0;
						}

						// 等待列数据渲染完毕
						$this.$nextTick(() => {
							$this.handleChange({
								detail: {
									column: column + 1,
									value: selectIndex
								}
							})
							if (isInitSelect) {
								// 有默认值，懒加载，处理选中的数据
								$this.selectValue.push(data[selectIndex])

							}
						})
					} else {
						// 下级无数据
						this.selectIndex.length = column + 1;
					}

				});
			},
			handleChange({
				detail
			}) {

				let {
					column,
					value
				} = detail
				const {
					children
				} = this.options

				let info = this.viewData[column][value]
				// 修复nvue问题
				// this.selectIndex[column] = value;
				this.selectIndex.splice(column, 1, value);

				// 懒加载，外部加载更新下一层级数据
				if (this.lazy && (!info || !info[children] || info[children].length < 1)) {
					this.handleLazyLoad(detail)
					return
				}

				// 下一层级
				let nextColumn = column + 1;
				if (!info || !info[children] || info[children].length < 1) {
					// 没有下一层级，则移除viewData 已有的数据；
					this.viewData.length = nextColumn;
					this.selectIndex.length = nextColumn;
					return
				}

				while (info[children] && info[children].length > 0) {

					let prevColumnData = this.viewData[nextColumn] || [];
					// 默认第一条数据
					if (this.viewData[nextColumn]) {
						// 修复nvue问题
						// this.viewData[nextColumn] = info[children]
						this.viewData.splice(nextColumn, 1, info[children])
					} else {
						this.viewData.push(info[children]);
					}
					// 重置其他column选择值位0
					// 不重置，相同子级同样选中
					let isSame = this.comparePrevColumnData(prevColumnData, this.viewData[nextColumn], nextColumn);
					if (!isSame) {
						this.selectIndex.splice(nextColumn, 1, 0)
					}
					nextColumn += 1;
					info = info[children][0]
				}

				this.viewData.length = nextColumn;
				this.selectIndex.length = nextColumn;

				// 调用一次默认选中，触发懒加载判断
				this.handleChange({
					detail: {
						column: nextColumn - 1,
						value: this.selectIndex[nextColumn - 1]
					}
				});

			},
			handleConfirm() {

				this.selectValue = this.selectIndex.map((val, index) => this.viewData[index][val])

				// 选中值
				// 最后一个选中对象
				let suffix = this.selectValue[this.selectValue.length - 1]
				if (this.checkStrictly && suffix.value === '') {
					this.selectValue.pop()
				}

				this.$emit('ok', this.selectValue[this.selectValue.length - 1], this.selectValue);
				this.handleCancel();
			},
			comparePrevColumnData(pre, cur, index) {
				const {
					value
				} = this.options

				if (!pre[index] || !cur[index]) {
					return false;
				}
				return pre[index][value] == cur[index][value]
			}
		}
	};
</script>

<style lang="less" scoped>
	@keyframes fadeIn {
		0% {
			opacity: 0;
		}

		100% {
			opacity: 1;
		}
	}

	@keyframes fadeOut {
		0% {
			opacity: 1;
		}

		100% {
			opacity: 0;
		}
	}

	@keyframes slideInUp {
		0% {
			transform: translate3d(0, 100%, 0);
			visibility: visible;
		}

		100% {
			transform: translate3d(0, 0, 0);
		}
	}

	@keyframes slideOutUp {
		0% {
			transform: translate3d(0, 0, 0);
			visibility: visible;
		}

		100% {
			transform: translate3d(0, 100%, 0);
		}
	}

	.ifun-tree-picker {
		.picker-label {
			.picker-label-placeholder {
				opacity: 0.4;
			}
		}

		.picker-popup {
			position: fixed;
			top: 0;
			left: 0;
			width: 100%;
			height: 100%;
			z-index: 999;

			.picker-popup-mask {
				position: absolute;
				left: 0;
				top: 0;
				width: 100%;
				height: 100%;
				background: rgba(0, 0, 0, 0.2);
				animation: fadeIn linear 300ms both;

				&.hidden {
					animation: fadeOut linear 300ms both;
				}
			}

			.picker-popup-body {
				height: 300px;
				left: 0;
				width: 100%;
				background: #fff;
				position: absolute;
				bottom: 0;
				animation: slideInUp linear 300ms both;

				&.hidden {
					animation: slideOutUp linear 300ms both;
				}

				/deep/.picker-header {
					height: 50px;
					display: flex;
					align-items: center;
					justify-content: space-between;
					padding: 0 10px;
					border-bottom: 1px solid #eee;
					gap: 15px;

					button {
						background: transparent;
						font-size: 14px;
						padding: 0;
						margin: 0;

						&::after {
							border: none;
						}

						&.cancel {
							color: #f04134;
						}

						&.submit {
							color: #108EE9;
						}
					}

					.header-search {
						flex: 1;
						height: 100%;
						display: flex;
						align-items: center;

						uni-input {
							height: 80%;
							width: 100%;
							border: 1px solid #eee;
							border-radius: 50px;
							padding: 5px 15px;
							box-sizing: border-box;
						}
					}
				}

				.picker-content {
					height: calc(100% - 50px);
					text-align: center;

					.picker-column-item {
						display: flex;
						align-items: center;
						justify-content: center;
					}
				}
			}
		}
	}
</style>